<?php

class StartWebsocketsServerCommand extends CConsoleCommand
{
    public $host    = '127.0.0.1';
    public $port    = '8000';
    public $null    = NULL;
    public $clients = array();

    protected $allowedOrigins = array('http://websockets-test.local');

    private function perform_handshaking($receved_header, $client_conn, $host, $port)
    {
        $headers = array();
        $lines   = preg_split("/\r\n/", $receved_header);

        foreach ($lines as $line) {
            $line = chop($line);

            if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
                $headers[$matches[1]] = $matches[2];
            }
        }

        if (in_array($headers['Origin'], $this->allowedOrigins)) {
            $secKey    = $headers['Sec-WebSocket-Key'];
            $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));

            //hand shaking header
            $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
                . "Upgrade: websocket\r\n"
                . "Connection: Upgrade\r\n"
                . "WebSocket-Origin: $host\r\n"
                . "WebSocket-Location: ws://$host:$port/\r\n"
                . "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
        } else {
            echo 'suck';
            /**
             * @todo return any header with code diverse 101
            **/
        }

        socket_write($client_conn,$upgrade,strlen($upgrade));
    }

    protected function send_message($msg)
    {
        // exit(print_r($this->clients));

        foreach($this->clients as $changed_socket)
        {
            socket_write($changed_socket, $msg, strlen($msg));
            // fwrite($changed_socket, $msg);
        }
        return true;
    }

    protected function mask($text)
    {
        $b1     = 0x80 | (0x1 & 0x0f);
        $length = strlen($text);

        if($length <= 125)
            $header = pack('CC', $b1, $length);
        elseif($length > 125 && $length < 65536)
            $header = pack('CCn', $b1, 126, $length);
        elseif($length >= 65536)
            $header = pack('CCNN', $b1, 127, $length);

        return $header . $text;
    }

    public function init()
    {
        parent::init();
    }

    public function run($args)
    {
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        if ($socket) {
            $this->log('Server created');
        } else {
            $this->log('Can\'t create server');
        }

        socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
        socket_bind($socket, 0, $this->port);
        socket_listen($socket);

        $this->clients = array($socket);

        while (true) {
            $changed = $this->clients;

            socket_select($changed, $changed, $this->null, 0, 10);

            if (in_array($socket, $changed)) {
                $this->log('Income connection');
                $socket_new = socket_accept($socket);
                $header     = socket_read($socket_new, 1024);

                $this->perform_handshaking($header, $socket_new, $this->host, $this->port);
                socket_getpeername($socket_new, $ip);

                $response = $this->mask(json_encode(array('type' => 'system', 'message' => $ip . ' connected')));

                // $this->send_message($response);

                $found_socket = array_search($socket, $changed);

                unset($changed[$found_socket]);
            }
        }

        socket_close($socket);
    }

    protected function log($message, $type = 'info')
    {
        echo date('Y-m-d H:i:s') . ' [' . ($type ? $type : 'error') . '] ' . $message . PHP_EOL;
    }
}
